<template>
    <div class="aside">
        <div class="aside_top">
            <div class="aside_brand" @click="router.push('/')">
                <div class="aside_logo"></div>
                亦或演示
            </div>
        </div>
        <el-button class="aside_add" type="primary" @click="conversation.add()">新对话</el-button>
        <el-menu :default-active="vm.conversation.id" class="aside_menu" v-if="vm.conversation">
            <el-menu-item
                class="aside_menu_item"
                v-for="(item, index) in vm.conversationList"
                :index="item.id"
                @click="message.list(item)"
            >
                {{ item.name }}
                <el-popconfirm
                    title="确定删除?"
                    @confirm="conversation.del(item.id, index)"
                    confirm-button-text="确认"
                    cancel-button-text="取消"
                >
                    <template #reference>
                        <el-icon class="aside_menu_icon" :size="16" @click.stop><RemoveFilled /></el-icon>
                    </template>
                </el-popconfirm>
            </el-menu-item>
        </el-menu>
    </div>
    <div class="container" v-if="vm.conversation">
        <div class="panel" ref="panel">
            <div class="panel_message_list">
                <template v-for="item in vm.messageList">
                    <div class="panel_message_reasoning" v-if="item.reasoningContent">
                        <div>{{ item.reasoningContent }}</div>
                    </div>
                    <div :class="'panel_message_' + item.role" v-if="item.content">
                        <div>{{ item.content }}</div>
                    </div>
                </template>
            </div>
        </div>
        <div class="input">
            <div class="input_box">
                <el-input
                    v-model="vm.question"
                    ref="input"
                    :rows="3"
                    type="textarea"
                    placeholder="给 DeepSeek 发送消息"
                    class="input_question"
                    @keydown.enter="message.keydown"
                />
            </div>
            <el-button type="primary" class="input_send_btn" @click="message.send()">发送</el-button>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { useRouter } from "vue-router";
import { reactive, ref, nextTick, onMounted, watch } from "vue";
import api, { type Conversation, type Message } from "@/api/api";
import { ElMessage } from "element-plus";

const router = useRouter();
const panel = ref<HTMLElement>(null);
const input = ref<HTMLElement>(null);

const vm = reactive({
    conversationList: [] as Conversation[], // 会话列表
    conversation: null as Conversation, // 当前选中的会话
    question: "" as string, // 用户的输入
    messageList: [] as Message[], // 消息列表
});

onMounted(() => {
    conversation.list();
});

// 会话方法
const conversation = {
    // 会话列表
    list() {
        api.conversationList().then(({ data }) => {
            vm.conversationList = data;
            if (data.length > 0) {
                message.list(data[0]);
            }
        });
    },
    // 创建会话
    add() {
        api.conversationAdd().then(({ data }) => {
            vm.conversationList.unshift(data);
            vm.conversation = data;
            vm.messageList = [];
            vm.question = "";
            nextTick(() => {
                input.value.focus();
            });
        });
    },
    // 删除会话
    del(id: string, index: number) {
        api.conversationDelete({ id }).then((res) => {
            if (res.status == 200) {
                vm.conversationList.splice(index, 1);
                // 当前还有会话，默认选中第一个
                if (vm.conversationList.length > 0) {
                    message.list(vm.conversationList[0]);
                }
                // 否则清空
                else {
                    vm.conversation = null;
                    vm.messageList = [];
                }
            } else {
                ElMessage({ message: res.message, type: "warning" });
            }
        });
    },
};

// 消息方法
const message = {
    // 消息列表
    list(conversation: Conversation) {
        vm.conversation = conversation;
        api.messageList({ conversationId: conversation.id }).then(({ data }) => {
            vm.messageList = data;
        });
    },
    // 发送消息
    send() {
        if (!vm.question) {
            return;
        }
        // 用户消息
        vm.messageList.push({ role: "user", content: vm.question });
        // 如果当前只有一条消息，进行会话重命名
        if (vm.messageList.length == 1) {
            api.conversationRename({ id: vm.conversation.id, content: vm.question }).then(({ data }) => {
                vm.conversation.name = data;
            });
        }
        // 发起对话
        let params = {
            content: vm.question,
            conversationId: vm.conversation.id,
        };
        vm.question = "";
        api.messageChat(params).then((response) => {
            const reader = response.body.getReader();
            let decoder = new TextDecoder("utf-8");
            // 大模型消息
            let message = reactive({
                role: "assistant",
                content: "",
                reasoningContent: "",
            });
            vm.messageList.push(message);

            function readStream() {
                reader.read().then(({ done, value }) => {
                    if (done) {
                        return;
                    }
                    const chunkList = decoder.decode(value).split("\n\n");
                    for (let chunk of chunkList) {
                        if (chunk.startsWith("reasoningContent:")) {
                            message.reasoningContent += chunk.substring(17);
                        }
                        if (chunk.startsWith("content:")) {
                            message.content += chunk.substring(8);
                        }
                    }
                    readStream();
                });
            }
            readStream();
        });
    },
    // 输入框回车
    keydown(e: KeyboardEvent) {
        if (!e.shiftKey) {
            e.preventDefault();
            message.send();
        }
    },
};

watch(
    () => vm.messageList,
    () => {
        nextTick(() => {
            if (panel.value) {
                panel.value.scrollTop = panel.value.scrollHeight;
            }
        });
    },
    { deep: true }
);
</script>
<style lang="css" scoped src="./ai.scss"></style>
